
import os
import json
from pathlib import Path
from dotenv import load_dotenv
load_dotenv()

def _load_runtime_env() -> dict:
    path = os.environ.get("RUNTIME_ENV_PATH")
    try:
        if os.path.exists(path):
            with open(path, "r", encoding="utf-8") as f:
                data = json.load(f)
                if isinstance(data, dict):
                    return data
    except Exception:
        pass
    return {}


def get_config_value(key: str, default=None):
    _RUNTIME_ENV = _load_runtime_env()
    
    if key in _RUNTIME_ENV:
        return _RUNTIME_ENV[key]
    return os.getenv(key, default)

def write_config_value(key: str, value: any):
    _RUNTIME_ENV = _load_runtime_env()
    _RUNTIME_ENV[key] = value
    path = os.environ.get("RUNTIME_ENV_PATH")
    with open(path, "w", encoding="utf-8") as f:
        json.dump(_RUNTIME_ENV, f, ensure_ascii=False, indent=4)

def extract_conversation(conversation: dict, output_type: str):
    """从对话载荷中提取信息。

    Args:
        conversation: 包含 'messages'（字典列表或带属性的对象）的映射。
        output_type: 'final' 返回模型的最终回答内容；'all' 返回完整的消息列表。

    Returns:
        对于 'final'：若找到则返回最终助手内容字符串，否则返回 None。
        对于 'all'：返回原始消息列表（若缺失则返回空列表）。
    """

    def get_field(obj, key, default=None):
        if isinstance(obj, dict):
            return obj.get(key, default)
        return getattr(obj, key, default)

    def get_nested(obj, path, default=None):
        current = obj
        for key in path:
            current = get_field(current, key, None)
            if current is None:
                return default
        return current

    messages = get_field(conversation, "messages", []) or []

    if output_type == "all":
        return messages
    # 目标：优先获取「最后一条正常结束且内容有效的消息」。
    # 这段代码通过 “优先取正常结束的有效消息” 和 “兜底取非工具相关的有效消息” 的逻辑，
    # 从消息列表中提取最适合作为 “最终输出” 的内容，
    # 适用于需要从对话历史中筛选结论性回复的场景（如 AI 对话的最终答案提取）。
    if output_type == "final":
        # 优先选择最后一条满足 finish_reason == 'stop' 且内容非空的消息
        # 倒序遍历消息列表（从最新的消息开始检查），确保找到的是 “最后一条符合条件” 的消息。
        for msg in reversed(messages):
            finish_reason = get_nested(msg, ["response_metadata", "finish_reason"])
            content = get_field(msg, "content")
            # finish_reason 是消息的结束标识，"stop" 通常意味着模型正常完成输出（非截断、非异常中断）。
            if finish_reason == "stop" and isinstance(content, str) and content.strip():
                return content

        # 兜底：最后一条非工具调用、非工具消息且内容非空的 AI 消息
        for msg in reversed(messages):
            content = get_field(msg, "content")
            additional_kwargs = get_field(msg, "additional_kwargs", {}) or {}
            tool_calls = None
            if isinstance(additional_kwargs, dict):
                tool_calls = additional_kwargs.get("tool_calls")
            else:
                tool_calls = getattr(additional_kwargs, "tool_calls", None)

            is_tool_invoke = isinstance(tool_calls, list)
            # 工具消息通常带有 tool_call_id 或 name（工具名称）
            has_tool_call_id = get_field(msg, "tool_call_id") is not None
            tool_name = get_field(msg, "name")
            is_tool_message = has_tool_call_id or isinstance(tool_name, str)

            if not is_tool_invoke and not is_tool_message and isinstance(content, str) and content.strip():
                return content

        return None

    raise ValueError("output_type 必须是 'final' 或 'all'")


def extract_tool_messages(conversation: dict):
    """从对话中提取所有 ToolMessage 类型的条目。

    ToolMessage 通过以下任一方式被启发式识别：
      - 非空的 'tool_call_id'，或
      - 字符串类型的 'name'（工具名称）且没有普通 AI 消息那样的 'finish_reason'

    同时支持字典格式和对象格式的消息。
    """

    def get_field(obj, key, default=None):
        if isinstance(obj, dict):
            return obj.get(key, default)
        return getattr(obj, key, default)

    def get_nested(obj, path, default=None):
        current = obj
        for key in path:
            current = get_field(current, key, None)
            if current is None:
                return default
        return current

    messages = get_field(conversation, "messages", []) or []
    tool_messages = []
    for msg in messages:
        tool_call_id = get_field(msg, "tool_call_id")
        name = get_field(msg, "name")
        finish_reason = get_nested(msg, ["response_metadata", "finish_reason"])  # present for AIMessage
        # Treat as ToolMessage if it carries a tool_call_id, or looks like a tool response
        if tool_call_id or (isinstance(name, str) and not finish_reason):
            tool_messages.append(msg)
    return tool_messages


def extract_first_tool_message_content(conversation: dict):
    """如果可用，返回第一个 ToolMessage 的内容；否则返回 None。"""
    msgs = extract_tool_messages(conversation)
    if not msgs:
        return None

    first = msgs[0]
    if isinstance(first, dict):
        return first.get("content")
    return getattr(first, "content", None)

